package infra

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"time"

	"gitee.com/h2ob/base"
	"gitee.com/h2ob/facada"
	"gitee.com/h2ob/infra/model"
	"github.com/boltdb/bolt"
	"github.com/uptrace/bun"
)

var (
	MOMENT_TABLE = "Moment"
)

func KvPut(table, key string, value []byte) error {
	err := gH2oDb.kv.Update(func(tx *bolt.Tx) error {
		buck, er := tx.CreateBucketIfNotExists([]byte(table))
		if er != nil {
			base.GLogger.Infof("Create %s buck error:%s", table, er)
		}
		bKey := []byte(key)
		er = buck.Put(bKey, []byte(value))
		if er != nil {
			base.GLogger.Infof("Put key %s error:%s", key, er)
		}
		return er
	})
	return err
}

func KvGet(table, key string) ([]byte, error) {
	var value []byte
	err := gH2oDb.kv.View(func(tx *bolt.Tx) error {
		buck := tx.Bucket([]byte(table))
		if buck == nil {
			return errors.New("no such value")
		}
		bKey := []byte(key)
		value = buck.Get(bKey)
		if value == nil {
			return errors.New("no such value")
		}
		return nil
	})
	return value, err
}

func FindByOpenId(oid string, otype string) (*model.HUser, error) {
	hoUser := new(model.HOpenUser)
	err := gH2oDb.db.NewSelect().Model(hoUser).Where("oid = ?", oid).Where("otype=?", otype).Scan(*gH2oDb.ctx)
	if err != nil {
		base.GLogger.Infof("Find hopenuser error:%s", err)
		return nil, err
	}
	user := new(model.HUser)
	err = gH2oDb.db.NewSelect().Model(user).Where("uid = ?", hoUser.Uid).Scan(*gH2oDb.ctx)
	if err != nil {
		base.GLogger.Infof("Find huser error:%s", err)
		return nil, err
	}
	return user, nil
}

func AddNewUser(nickname string, openId string, otype string, avatar string) (*model.HUser, error) {
	huid, err := getUidSeg()
	if err != nil {
		return nil, err
	}
	var huser *model.HUser
	err = gH2oDb.db.RunInTx(*gH2oDb.ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
		uid := huid.Minv + huid.Off + 1
		rer := updateUidSeg(huid, tx, ctx)
		if rer != nil {
			return rer
		}
		rer = addNewOpenUser(uid, openId, otype, tx, ctx)
		if rer != nil {
			return rer
		}
		avatar = fmt.Sprintf("https://askdao.top/h2o/oss/avatar?q=%d%s", uid, GetFileType(avatar))
		huser = &model.HUser{Uid: uint64(uid), Nickname: nickname, Avatar: avatar}
		rer = addNewUser2(huser, tx, ctx)
		if rer != nil {
			huser = nil
			return rer
		}
		return nil
	})
	return huser, err
}

func AddMoment(uid, nickname string, moment *facada.H2oMoment) error {
	mid := base.GetUuid()
	hmt := model.HMoment{Uid: strToInt64(uid), Score: 0, Loves: 0, Views: 0}
	hmt.Mid = mid
	hmt.Title = moment.Title
	hmt.Tag = "moment"
	hmt.Img = fmt.Sprintf("https://askdao.top/h2o/oss/get?tag=moment&q=%s", moment.Img)
	hmt.Location = moment.Location
	hmt.Author = nickname
	hmt.Expire = timeAddNDay(time.Now(), 7)
	err := gH2oDb.db.RunInTx(*gH2oDb.ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
		r, er := tx.NewInsert().Model(&hmt).Column("uid", "score", "loves", "views", "mid", "title", "tag", "author", "img", "location", "expire").Ignore().Exec(ctx)
		if er != nil {
			return er
		}
		ar, _ := r.RowsAffected()
		if ar == 0 {
			base.GLogger.Info("affect zero")
			return errors.New("affect zero")
		}
		er = KvPut(MOMENT_TABLE, mid, []byte(moment.Content))
		return er
	})
	if err != nil {
		base.GLogger.Infof("AddMoment error:%s", err)
	}
	return err
}

func RecommendMoment(page, pageSize int) ([]model.HMoment, error) {
	hmtList := make([]model.HMoment, 0)
	ctx := gH2oDb.ctx
	off := (page - 1) * pageSize
	err := gH2oDb.db.NewRaw("SELECT uid,score,loves,views,mid,title,tag,author,img,location,ctime FROM ? WHERE expire >= NOW() ORDER BY hid DESC LIMIT ? OFFSET ?",
		bun.Ident("hmoment"), pageSize, off).Scan(*ctx, &hmtList)
	if err != nil {
		base.GLogger.Infof("RecommendMoment error:%s", err)
		return nil, err
	}
	return hmtList, err
}

func GetMoment(mid string) (*facada.H2oEvent, error) {
	hmt := new(model.HMoment)
	ctx := *gH2oDb.ctx
	err := gH2oDb.db.NewSelect().Model(hmt).Where("mid = ?", mid).Scan(ctx)
	if err != nil {
		base.GLogger.Infof("GetMoment error:%s", err)
		return nil, err
	}
	he := &facada.H2oEvent{Id: hmt.Mid, Title: hmt.Title,
		Img: hmt.Img, Tag: hmt.Tag, Author: hmt.Author,
		Time:  base.TimeFormat(hmt.Ctime),
		Views: hmt.Views, Loves: hmt.Loves}
	b, err := KvGet(MOMENT_TABLE, mid)
	if err != nil {
		base.GLogger.Infof("GetMoment error:%s", err)
		return nil, err
	}
	he.Content = string(b)
	return he, nil
}

func UpdateMoment(hup *facada.H2oMomentUpdate) error {
	r, err := gH2oDb.db.NewRaw("UPDATE ? SET loves=loves+?,views=views+? WHERE mid = ?", bun.Ident("hmoment"), hup.Loves, hup.Views, hup.Id).Exec(*gH2oDb.ctx)
	if err != nil {
		base.GLogger.Infof("UpdateMoment error:%s", err)
		return err
	}
	n, _ := r.RowsAffected()
	if n > 0 {
		return nil
	}
	base.GLogger.Infof("UpdateMoment affecte:%d rows", n)
	return nil
}
